#include <iostream>
#include <ostream>
#include <chrono>

template <typename T>
class Matrix
{
    friend std::ostream& operator<<(std::ostream& out, const Matrix<T>& matrix);
public:
    Matrix(int theRows = 0, int theColumns = 0, bool init_zero=false);
    Matrix(const Matrix<T>& matrix);
    ~Matrix(){delete [] m_element;}

    int rows()const {return m_rows;}
    int columns()const {return m_columns;}
    T& operator()(int i, int j)const;
    Matrix<T>& operator=(const Matrix<T>& matrix);
    Matrix<T> operator+()const;
    Matrix<T> operator+(const Matrix<T>& matrix)const;
    Matrix<T> operator-()const;
    Matrix<T> operator-(const Matrix<T>& matrix)const;
    Matrix<T> operator*(const Matrix<T>& matrix)const;
    Matrix<T>& operator+=(const T& x);

private:
    int m_rows, m_columns;
    T* m_element;
};

template <typename T>
Matrix<T>::Matrix(int theRows, int theColumns, bool init_zero)
{
    if(theRows < 0 || theColumns < 0){
        std::cerr<< "the rows and the columns must be >=0"<<std::endl;
        return;
    }

    m_rows = theRows;
    m_columns = theColumns;
    if(init_zero)
        m_element = new T[m_rows * m_columns]();
    else
        m_element = new T[m_rows * m_columns];
}

template<typename T>
Matrix<T>::Matrix(const Matrix<T>& matrix)
{
    m_rows = matrix.m_rows;
    m_columns = matrix.m_columns;
    m_element = new T[m_rows * m_columns];
    std::copy(matrix.m_element,
              matrix.m_element + m_rows * m_columns,
              m_element);
}

template<typename T>
T& Matrix<T>::operator()(int i, int j)const
{
    if(i < 1 || i > m_rows|| j < 1 || j > m_columns){
        std::cerr<<"out of range."<<std::endl;
        throw "out of range.";
    }
    return m_element[(i-1) * m_columns + j - 1];

}

template <typename T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& matrix)
{
    if(this != &matrix){
        delete [] m_element;
        m_rows = matrix.m_rows;
        m_columns = matrix.m_columns;
        m_element = new T[m_rows * m_columns];
        std::copy(matrix.m_element,
                  matrix.m_element + m_rows * m_columns,
                  m_element);
    }

    return *this;

}

template <typename T>
Matrix<T> Matrix<T>::operator+()const
{
    Matrix<T> result(m_rows, m_columns);
    for(int i = 0; i < m_rows * m_columns; ++i){
        result.m_element[i] += m_element[i];
    }

    return result;
}

int main(int argc, char *argv[])
{
    long long number = 10000000;

    std::cout<<"Test for not () end of new"<<std::endl;

    auto start = std::chrono::system_clock::now();
    int* a = new int[number];
    auto end = std::chrono::system_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout<<elapsed.count()<<"ms"<<std::endl;

    for(int i = 0; i < 5; ++i){
        std::cout<<a[i]<<std::endl;
    }

    std::cout<<"Test for () end of new"<<std::endl;
    start = std::chrono::system_clock::now();
    int *b = new int[number]();
    end = std::chrono::system_clock::now();
    elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout<<elapsed.count()<<"ms"<<std::endl;

    for(int i = 0; i < 5; ++i){
        std::cout<<b[i]<<std::endl;
    }
    return 0;
}
